package com.alibaba.druid.translation;

import com.alibaba.druid.DbType;
import com.alibaba.druid.filter.FilterAdapter;
import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.proxy.jdbc.*;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.alibaba.druid.util.JdbcUtils;

import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author jiangtao
 */
public class TranslationFilter extends FilterAdapter {

    private final static Log LOG = LogFactory.getLog(TranslationFilter.class);
    private final Lock lock = new ReentrantLock();

    public final static String SOURCE_SQL_TYPE = "druid.translation.sourceSqlType";

    private DbType dbType;

    private String sourceSqlType;

    private TranslationConfig config;

    public TranslationFilter() {
    }

    @Override
    public void init(DataSourceProxy dataSource) {
        lock.lock();
        try {
            if (dataSource.getDbType() != null) {
                this.dbType = DbType.of(dataSource.getDbType());
            } else {
                this.dbType = DbType.of(JdbcUtils.getDbType(dataSource.getRawJdbcUrl(), ""));
            }
            configFromProperties(dataSource.getConnectProperties());
            configFromProperties(System.getProperties());
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void configFromProperties(Properties properties) {
        if (properties == null) {
            return;
        }

        this.sourceSqlType = properties.getProperty(SOURCE_SQL_TYPE);

        if(null != config) {
            this.sourceSqlType = config.getSourceSqlType();
        }
    }

    @Override
    public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql) throws SQLException {
        return super.connection_prepareStatement(chain, connection, translationSql(sql));
    }

    @Override
    public CallableStatementProxy connection_prepareCall(FilterChain chain, ConnectionProxy connection, String sql) throws SQLException {
        return super.connection_prepareCall(chain, connection, translationSql(sql));
    }

    @Override
    public CallableStatementProxy connection_prepareCall(FilterChain chain, ConnectionProxy connection, String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return super.connection_prepareCall(chain, connection, translationSql(sql), resultSetType, resultSetConcurrency);
    }

    @Override
    public CallableStatementProxy connection_prepareCall(FilterChain chain, ConnectionProxy connection, String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return super.connection_prepareCall(chain, connection, translationSql(sql), resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    @Override
    public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql, int autoGeneratedKeys) throws SQLException {
        return super.connection_prepareStatement(chain, connection, translationSql(sql), autoGeneratedKeys);
    }

    @Override
    public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return super.connection_prepareStatement(chain, connection, translationSql(sql), resultSetType, resultSetConcurrency);
    }

    @Override
    public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return super.connection_prepareStatement(chain, connection, translationSql(sql), resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    @Override
    public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql, int[] columnIndexes) throws SQLException {
        return super.connection_prepareStatement(chain, connection, translationSql(sql), columnIndexes);
    }

    @Override
    public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql, String[] columnNames) throws SQLException {
        return super.connection_prepareStatement(chain, connection, translationSql(sql), columnNames);
    }

    @Override
    public void statement_addBatch(FilterChain chain, StatementProxy statement, String sql) throws SQLException {
        super.statement_addBatch(chain, statement, translationSql(sql));
    }

    @Override
    public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql) throws SQLException {
        return super.statement_execute(chain, statement, translationSql(sql));
    }

    @Override
    public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql, int autoGeneratedKeys) throws SQLException {
        return super.statement_execute(chain, statement, translationSql(sql), autoGeneratedKeys);
    }

    @Override
    public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql, int[] columnIndexes) throws SQLException {
        return super.statement_execute(chain, statement, translationSql(sql), columnIndexes);
    }

    @Override
    public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql, String[] columnNames) throws SQLException {
        return super.statement_execute(chain, statement, translationSql(sql), columnNames);
    }

    @Override
    public ResultSetProxy statement_executeQuery(FilterChain chain, StatementProxy statement, String sql) throws SQLException {
        return super.statement_executeQuery(chain, statement, translationSql(sql));
    }

    @Override
    public int statement_executeUpdate(FilterChain chain, StatementProxy statement, String sql) throws SQLException {
        return super.statement_executeUpdate(chain, statement, translationSql(sql));
    }

    @Override
    public int statement_executeUpdate(FilterChain chain, StatementProxy statement, String sql, int autoGeneratedKeys) throws SQLException {
        return super.statement_executeUpdate(chain, statement, translationSql(sql), autoGeneratedKeys);
    }

    @Override
    public int statement_executeUpdate(FilterChain chain, StatementProxy statement, String sql, int[] columnIndexes) throws SQLException {
        return super.statement_executeUpdate(chain, statement, translationSql(sql), columnIndexes);
    }

    @Override
    public int statement_executeUpdate(FilterChain chain, StatementProxy statement, String sql, String[] columnNames) throws SQLException {
        return super.statement_executeUpdate(chain, statement, translationSql(sql), columnNames);
    }

    /**
     * sql 转换
     *
     * @param sql 语句
     * @return 转换结果
     */
    public String translationSql(String sql) {
        LOG.info("sql 将由 " + this.sourceSqlType + " 转换为 " + this.dbType);
        List<SQLStatement> statementList = SQLUtils.parseStatements(sql, this.sourceSqlType);
        sql = SQLUtils.toSQLString(statementList, this.dbType);
        LOG.info("Kingbase SQL : " + sql);
        return sql;
    }

    public void setConfig(TranslationConfig config) {
        this.config = config;
    }
}
